﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TrackerRealm.Jazz.Common
{
    /// <summary>
    /// '<see cref="jTimestamp"/>' is a unique id across a single server nexus and provides a time in UTC (to within the accuracy
    /// of DateTime) when the stamp was created.
    /// <para>The granularity of the time stamp is 100 ns.  If 2 time stamps are created within a 100ns period the 2nd
    /// time stamp be stamped 100ns later.</para>
    /// <para>Use 'NextTimeStamp' to create a time stamp.</para>
    /// </summary>
    public struct jTimestamp: IComparable, IConvertible
    {
        #region Static Constructor
        /// <summary>
        /// Static Constructor
        /// </summary>
        static jTimestamp()
        {
            jTimestamp.empty.tick = 0;
        }
        #endregion
        #region Static Members
        private static jTimestamp empty;
        /// <summary>
        /// 
        /// </summary>
        public static jTimestamp Empty
        {
            get { return empty; }
        }
        /// <summary>
        /// Parses a string into a '<see cref="jTimestamp"/>'.
        /// </summary>
        /// <param name="timestampString"></param>
        public static jTimestamp Parse(string timestampString)
        {
            if (timestampString == null || timestampString == "") return jTimestamp.Empty;
            jTimestamp ts;
            ts.tick = long.Parse(timestampString);
            return ts;
        }
        #region NextTimeStamp
        private static long timeStampTick = DateTime.Now.ToUniversalTime().Ticks;
        /// <summary>
        /// Returns the next time stamp;
        /// </summary>
        /// <returns></returns>
        public static jTimestamp NextTimeStamp()
        {
            jTimestamp ts;
            ts.tick = DateTime.Now.ToUniversalTime().Ticks;
            if (ts.tick <= timeStampTick)
            {
                // make sure the time stamp are unique - add 100 ns
                ts.tick = timeStampTick + 1;
            }
            timeStampTick = ts.tick;
            return ts;
        }
        #endregion
        #endregion

        private long tick;

        #region IsEmpty
        /// <summary>
        /// Returns true if this '<see cref="jTimestamp"/>' is empty.
        /// </summary>
        public bool IsEmpty
        {
            get { return (this == jTimestamp.Empty); }
        }
        #endregion
        #region Tick
        /// <summary>
        /// Returns the 'Tick' of this '<see cref="jTimestamp"/>'.
        /// </summary>
        public long Tick
        {
            get { return this.tick; }
        }
        #endregion
        #region DateTime
        /// <summary>
        /// Returns the DateTime when this time stamp was created.
        /// <para>See '<see cref="jTimestamp"/>' struc comments for caveats.</para>
        /// </summary>
        public DateTime DateTime
        {
            get { return new DateTime(this.tick, DateTimeKind.Utc); }
        }
        #endregion
        #region Equality and GetHashCode methods
        /// <summary>
        /// Overloaded Equality Operator (==) for "jTimestamp".
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(jTimestamp a, jTimestamp b)
        {
            // need to compare 
            return CompareEverything(ref a, ref b);

        }
        /// <summary>
        /// Returns true if all fields are equal.  .
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static bool CompareEverything(ref jTimestamp a, ref jTimestamp b)
        {
            if (a.tick != b.tick) return false;
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.tick.GetHashCode();
        }
        /// <summary>
        /// Overloaded Not Equality Operator (!=) for <see cref="jTimestamp"/>.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(jTimestamp a, jTimestamp b)
        {

            return (!jTimestamp.CompareEverything(ref a, ref b));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (!(obj is jTimestamp)) return false;
            return (this == ((jTimestamp)obj));
        }
        #endregion
        #region ToString
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.tick.ToString() ;
        }
        #endregion
        #region Stamp
        /// <summary>
        /// Returns a string representation of this '<see cref="jTimestamp"/>'.
        /// </summary>
        public string Stamp
        {
            get { return this.ToString(); }
        }
        #endregion

        #region IComparable Members
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            if (!(obj is jTimestamp))
                throw new ArgumentException();
            jTimestamp rhs = (jTimestamp)obj;
            return this.tick.CompareTo(rhs.tick);
        }

        #endregion

        #region IConvertible Members
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public TypeCode GetTypeCode()
        {
            if (this.IsEmpty) return TypeCode.Empty;
            return TypeCode.Object;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public bool ToBoolean(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public byte ToByte(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public char ToChar(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public DateTime ToDateTime(IFormatProvider provider)
        {
            return new DateTime(this.tick);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public decimal ToDecimal(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public double ToDouble(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public short ToInt16(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public int ToInt32(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public long ToInt64(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public sbyte ToSByte(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public float ToSingle(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public string ToString(IFormatProvider provider)
        {
            return this.Stamp;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="conversionType"></param>
        /// <param name="provider"></param>
        /// <returns></returns>
        public object ToType(Type conversionType, IFormatProvider provider)
        {
            if (conversionType == typeof(jTimestamp))
                return this.ToString(null);
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public ushort ToUInt16(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public uint ToUInt32(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public ulong ToUInt64(IFormatProvider provider)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
